package practices;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import static java.sql.DriverManager.getConnection;

/**
 * @Description @TODO
 * @Date 2025/4/22 13:44
 * @Created by weimeiling
 */
public class LockedStuExample {


}


/**
 * 悲观锁（Pessimistic Lock）
 * 是一种在并发编程和数据库操作中常用的锁机制。
 * 它的核心思想是“悲观”地认为在自己操作数据的过程中，其他线程或事务很可能会修改数据，所以在操作数据之前就先对数据进行加锁，
 * 防止其他线程或事务对数据进行修改，直到操作完成并释放锁。
 * <p>
 * 应用场景
 * 并发编程：在多线程环境下，多个线程可能会同时访问和修改共享资源，使用悲观锁可以保证同一时间只有一个线程能够访问和修改该资源，避免数据不一致的问题。
 * 数据库操作：在事务处理中，当一个事务需要对某些数据进行修改时，使用悲观锁可以防止其他事务在该事务提交之前对这些数据进行修改。
 */

class PessimisticLockStuExample1 {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();

    public void increment() {
        // 加锁
        lock.lock();
        try {
            count++;
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}

class PessimisticLockStuExample2 {
    private int count = 0;

    // 使用 synchronized 关键字实现悲观锁
    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

/**
 * 乐观锁
 * 概念
 * 乐观锁是一种并发控制策略，其核心思想是乐观地认为在数据操作过程中，其他线程或事务不会对数据进行修改。
 * 因此，在操作数据时不会加锁，仅在更新数据时检查数据是否被其他操作改变。若数据未变，则执行更新；若已变，通常会让用户重试或放弃操作。
 * <p>
 * 实现方式
 * 1. 版本号机制
 * 在数据库表中添加一个版本号字段，每次数据更新时版本号加 1。更新数据时，检查当前版本号是否与数据库中的版本号一致，一致则更新并提升版本号，不一致则表示数据已被修改。
 * 2. CAS（Compare-And-Swap）
 * CAS 是一种无锁算法，包含三个操作数：内存位置（V）、预期原值（A）和新值（B）。如果内存位置的值与预期原值相匹配，处理器会自动将该位置值更新为新值；
 * 否则，不做操作。Java 的 java.util.concurrent.atomic 包提供了一系列基于 CAS 实现的原子类。
 * <p>
 * 适用场景和优缺点
 * 适用场景
 * 读多写少的场景，此时数据冲突概率低，乐观锁可减少锁开销，提升并发性能。
 * 对响应时间敏感的系统，乐观锁无需加锁等待，能提高系统响应速度。
 * 优点
 * 并发性能高，无需加锁，减少线程阻塞和上下文切换开销。
 * 实现简单，代码复杂度相对较低。
 * 缺点
 * 若写操作频繁，数据冲突概率高，会导致大量重试，影响性能。
 * 只能保证单个变量或操作的原子性，对于复杂事务处理能力有限。
 */

/**
 * CREATE TABLE user (
 id INT PRIMARY KEY,
 name VARCHAR(50),
 age INT,
 version INT DEFAULT 0
 );

 */


class OptimisticLockWithVersion {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database";
    private static final String DB_USER = "username";
    private static final String DB_PASSWORD = "password";

    public boolean updateUser(int userId, String newName, int newAge) {
        Connection conn = null;
        PreparedStatement selectStmt = null;
        PreparedStatement updateStmt = null;
        ResultSet rs = null;
        int currentVersion = 0;

        try {
            conn = getConnection(DB_URL, DB_USER, DB_PASSWORD);

            // 查询当前数据和版本号
            String selectSql = "SELECT name, age, version FROM user WHERE id = ?";
            selectStmt = conn.prepareStatement(selectSql);
            selectStmt.setInt(1, userId);
            rs = selectStmt.executeQuery();
            if (rs.next()) {
                currentVersion = rs.getInt("version");
            }

            // 尝试更新数据
            String updateSql = "UPDATE user SET name = ?, age = ?, version = version + 1 WHERE id = ? AND version = ?";
            updateStmt = conn.prepareStatement(updateSql);
            updateStmt.setString(1, newName);
            updateStmt.setInt(2, newAge);
            updateStmt.setInt(3, userId);
            updateStmt.setInt(4, currentVersion);

            int rowsAffected = updateStmt.executeUpdate();
            return rowsAffected > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null) rs.close();
                if (selectStmt != null) selectStmt.close();
                if (updateStmt != null) updateStmt.close();
                if (conn != null) conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
}

class OptimisticLockWithCAS {
    private final AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        int current;
        do {
            current = count.get();
        } while (!count.compareAndSet(current, current + 1));
    }

    public int getCount() {
        return count.get();
    }
}
/**
 * 在 Java 中，synchronized 和 ReentrantLock 都是可重入锁，
 * 这意味着一个线程可以多次获取同一把锁，而不会被阻塞。
 * 可重入锁的实现方式是通过记录线程获取锁的次数，每次获取锁时，
 * 线程会检查自己是否已经持有该锁，如果已经持有，则增加持有次数；
 * 释放锁时，线程会减少持有次数，如果持有次数为 0，则释放锁。
 * 可重入锁的主要作用是避免死锁和提高程序的可读性和可维护性。
 * 区别
 * 1. 实现方式
 * synchronized 是 Java 语言内置的关键字，
 * 而 ReentrantLock 是一个类，需要手动创建实例并调用相关方法来实现锁的获取和释放。
 * 2. 功能
 * synchronized 是一种独占锁，同一时间只能有一个线程持有锁，其他线程需要等待。
 * ReentrantLock 是一种可重入锁，可以在同一线程中多次获取锁，不会被阻塞。
 * 4. 锁的粒度
 * synchronized 只能锁定整个方法或代码块，无法精确控制锁的粒度。
 * ReentrantLock 可以精确控制锁的粒度，可以锁定代码块中的某一部分。
 * 5. 异常处理
 * synchronized 在发生异常时会自动释放锁，而 ReentrantLock 需要手动释放锁。
 * 6. 锁的类型
 * synchronized 是一种非公平锁，多个线程可能会同时竞争锁。
 * ReentrantLock 可以是公平锁或非公平锁，可以通过构造函数指定锁的类型。
 * 7. 锁的状态
 * synchronized 只能在锁定和释放锁的过程中获取锁的状态，无法直接获取锁的状态。
 * ReentrantLock 可以直接获取锁的状态，可以通过 isLocked() 方法判断锁是否被持有。
 * 8. 锁的释放
 * synchronized 在代码块执行完毕后会自动释放锁，无需手动释放锁。
 * ReentrantLock 需要手动释放锁，可以通过 unlock() 方法释放锁。
 * 9. 锁的等待
 * synchronized 在等待锁时会一直阻塞，直到获取到锁。
 * ReentrantLock 可以通过 tryLock() 方法尝试获取锁，如果获取不到锁，可以选择等待一段时间或放弃等待。
 * 10. 锁的中断
 * synchronized 在等待锁时无法中断，只能等待锁的释放。
 * ReentrantLock 可以通过 interrupt() 方法中断等待锁的线程。
 * 11. 锁的条件
 * synchronized 无法使用条件变量，只能使用 wait() 和 notify() 等方法。
 * ReentrantLock 可以使用条件变量，通过 Condition 接口实现条件变量的使用。
 * 12. 锁的可重入性
 * synchronized 是可重入锁，可以在同一线程中多次获取锁，不会被阻塞。
 * ReentrantLock 也是可重入锁，可以在同一线程中多次获取锁，不会被阻塞。
 * 13. 锁的公平性
 * synchronized 是非公平锁，多个线程可能会同时竞争锁。
 * ReentrantLock 可以是公平锁或非公平锁，可以通过构造函数指定锁的类型。
 * 16. 锁的条件变量
 * synchronized 无法使用条件变量，只能使用 wait() 和 notify() 等方法。
 * ReentrantLock 可以使用条件变量，通过 Condition 接口实现条件变量的使用。
 *
 *
 * 如何选择
 *
 * 1. 如果能不用最好既不使用Lock 也不使用 synchronized。因为在许多情
 * 况下你可以使用java.util.concurrent 包中的机制，它会为你处理所有
 * 的加锁和解锁操作，也就是推荐优先使用工具类来加解锁
 * 2. 如果 synchronized 关键字适合你的程序，那么请尽量使用它，这样可
 * 以减少编写代码的数量，减少出错的概率。因为一旦忘记在 finally里
 * unlock，代码可能会出很大的问题，而使用 synchronized 更安全
 * 3. 如果特别需要 Lock 的特殊功能，比如尝试获取锁、可中断、超时功能等
 * 才使用 Lock
 */

/**
 * 概念
 * 在并发编程里，公平锁和非公平锁是两种不同的锁获取策略，主要用于控制多个线程对共享资源的访问顺序。
 *
 * 公平锁
 * 公平锁遵循“先来先到”的原则，线程按照请求锁的先后顺序依次获取锁。当一个线程请求锁时，如果锁已经被其他线程持有，该线程会进入等待队列，排在队列尾部，只有当队列前面的线程都释放锁后，它才能获取锁。
 *
 * 非公平锁
 * 非公平锁不保证线程获取锁的顺序，当锁被释放时，任何等待的线程都有机会竞争获取锁，而不是按照请求顺序。新请求锁的线程可能会“插队”，直接获取到锁，而无需进入等待队列。
 */

class FairLockExample {
    private final ReentrantLock fairLock = new ReentrantLock(true); // true 表示创建公平锁

    public void performTask() {
        fairLock.lock();
        try {
            // 模拟业务操作
            System.out.println(Thread.currentThread().getName() + " 获取到公平锁，执行任务");
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            fairLock.unlock();
        }
    }

    public static void main(String[] args) {
        FairLockExample example = new FairLockExample();
        for (int i = 0; i < 5; i++) {
            new Thread(example::performTask, "Thread-" + i).start();
        }
    }
}


class NonFairLockExample {
    private final ReentrantLock nonFairLock = new ReentrantLock(); // 默认是非公平锁

    public void performTask() {
        nonFairLock.lock();
        try {
            // 模拟业务操作
            System.out.println(Thread.currentThread().getName() + " 获取到非公平锁，执行任务");
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            nonFairLock.unlock();
        }
    }

    public static void main(String[] args) {
        NonFairLockExample example = new NonFairLockExample();
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            executor.submit(example::performTask);
        }

        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }

    /**
     * 读写锁
     * 读写锁是Java并发编程中一个重要的概念，它允许多个线程同时读共享资源，但只允许一个线程写共享资源。
     * 读写锁的实现类是ReentrantReadWriteLock，它提供了两个锁：读锁和写锁。
     * 读锁：允许多个线程同时读共享资源，但当有线程写入共享资源时，其他线程必须等待。
     * 写锁：只允许一个线程写入共享资源，当有线程读取共享资源时，其他线程必须等待。
     * 读写锁的优点是，它可以提高并发读的效率，因为多个线程可以同时获取读锁，而写锁则需要等待。
     * 读写锁的缺点是，如果写锁被占用，则所有读锁都会被阻塞。
     * 读写锁的适用场景：
     * 1. 读多写少场景：多个线程同时读取共享资源，但只有一个线程写入共享资源。
     * 2. 缓存场景：缓存中存储的数据可能被多个线程同时访问，但只有少数线程会修改数据。
     * 3. 数据库连接池场景：数据库连接池中存储的数据可能被多个线程同时访问，但只有少数线程会修改数据。
     * 4. 文件读写场景：文件读写场景中，多个线程可以同时读取文件，但只有一个线程可以写入文件。
     */
    class ReadWriteLockExample {
        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        private final Map<String, String> cache = new HashMap<>();

    }

}
